package com.gzz.orchid.common.webmvc.configuration;

import com.gzz.orchid.common.webmvc.securityjwt.DelegateAccessDeniedHandler;
import com.gzz.orchid.common.webmvc.securityjwt.DelegateAuthenticationEntryPointHandler;
import com.gzz.orchid.common.webmvc.securityjwt.JwtRequestFilter;
import jakarta.annotation.PostConstruct;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author higmd
 */
//标明这个类为配置类，spring应用程序一启动，类中的been 就会被初始化在spring容器中
@Configuration
//开启spring security 自定义配置
@EnableWebSecurity
//会拦截注解了@PreAuthrize，@Secured注解的配置.
public class WebSecurityConfig {
    /**
     * 创建密码编码器
     * 使用BCryptPasswordEncoder加密 10次 生成密码
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        //return NoOpPasswordEncoder.getInstance();
        // 更灵活的密码加密器
        //return PasswordEncoderFactories.createDelegatingPasswordEncoder();
        return new BCryptPasswordEncoder(10);
    }

    /**
     * 身份验证管理器
     */
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }


    public AuthenticationManager adminAuthenticationManager() throws Exception {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 用户详情服务
        provider.setUserDetailsService(userDetailsService());
        // 密码编码器
        provider.setPasswordEncoder(passwordEncoder());
        var manager = new ProviderManager(provider);
        manager.setEraseCredentialsAfterAuthentication(false);
        return manager;
    }
    /*
      忽略不需要认证的地址
     */
    //    @Bean
    //    public WebSecurityCustomizer ignoreCustomize(){
    //        return (web) -> web.ignoring()
    //                .requestMatchers("/css/**","/js/**","/images/**","/static/**","actuator/**");
    //    }

    /**
     * 配置过滤器链
     */
    @Bean
    @ConditionalOnProperty(prefix = "spring.security", name = "enabled", havingValue = "true")
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        System.out.println("指定接口请求  SecurityFilterChain");
        // 配置HTTP头信息
        httpSecurity.headers(headers -> headers
                        .contentSecurityPolicy(csp -> csp.policyDirectives("Default - G-zz@studio'"))
                        .httpStrictTransportSecurity(hsts -> hsts.includeSubDomains(true))
                        .frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)
                        .cacheControl(HeadersConfigurer.CacheControlConfig::disable)
                )
                // 禁用basic明文验证
                .httpBasic(AbstractHttpConfigurer::disable)
                // 关闭跨站请求防护, 前后端分离架构不需要csrf保护
                .csrf(Customizer.withDefaults())
                // 禁用默认登录页
                .formLogin(AbstractHttpConfigurer::disable)
                // 禁用默认登出页
                .logout(AbstractHttpConfigurer::disable)
                // 前后端分离是无状态的，不需要session了，直接禁用。
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                // 基于token，所以不需要session
                //.sessionManagement(AbstractHttpConfigurer::disable)
                // 配置权限
                .authorizeHttpRequests(auth -> auth
                        //  允许访问匿名地址，
                        .requestMatchers("/favicon.ico").permitAll()
                        .requestMatchers("/webflux/**").permitAll()
                        .requestMatchers("/example/**").permitAll()
                        .requestMatchers("/manage/**").permitAll()
                        .requestMatchers("/monitor/**").permitAll()
                        //允许跨域请求的OPTIONS请求
                        .requestMatchers(HttpMethod.OPTIONS).permitAll()
                        // 其他地址的访问，都需要验证后才可以访问
                        .anyRequest()
                        .authenticated()
                )
                //.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt->jwt.decoder()));
                //.userDetailsService(userDetailsService())
                // 禁用CSRF 开启跨域
                .cors(Customizer.withDefaults())

                //.authenticationManager(new SecurityAuthenticationManager());
                // 自定义权限拒绝处理类
                // 处理 SpringSecurity 异常响应结果。响应数据的结构，改成业务统一的JSON结构。不要框架默认的响应结构
                .exceptionHandling(handling ->
                        handling
                                // 认证失败异常
                                .authenticationEntryPoint(new DelegateAuthenticationEntryPointHandler())
                                // 鉴权失败异常
                                .accessDeniedHandler(new DelegateAccessDeniedHandler())
                )
                // 添加-自定义权限拦截器JWT过滤器
                .addFilterBefore(new JwtRequestFilter(), UsernamePasswordAuthenticationFilter.class);
        return httpSecurity.build();
    }


    /**
     * 配置全开放过滤器链
     */
    @Bean
    @ConditionalOnProperty(prefix = "spring.security", name = "enabled", havingValue = "false", matchIfMissing = true)
    public SecurityFilterChain allowFilterChain(HttpSecurity httpSecurity) throws Exception {
        System.out.println("开启所有接口请求");
        httpSecurity
                // 禁用basic明文验证
                .httpBasic(AbstractHttpConfigurer::disable)
                // 关闭跨站请求防护, 前后端分离架构不需要csrf保护
                .csrf(AbstractHttpConfigurer::disable)
                // 禁用默认登录页
                .formLogin(AbstractHttpConfigurer::disable)
                // 基于token，所以不需要session
                .sessionManagement(AbstractHttpConfigurer::disable)
                // 配置权限
                .authorizeHttpRequests(authorize -> authorize
                        //  允许访问匿名地址，
                        //允许跨域请求的OPTIONS请求
                        .requestMatchers(HttpMethod.OPTIONS).permitAll()
                        // 其他地址的访问，都需要验证后才可以访问
                        .anyRequest().permitAll()
                )
                //.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt->jwt.decoder()));
                //.userDetailsService(userDetailsService())
                // 禁用CSRF 开启跨域
                .cors(Customizer.withDefaults());
        return httpSecurity.build();
    }

    /**
     * 创建用户管理器
     */
    @Bean
    public UserDetailsService userDetailsService() {
        //1、创建基于内存的用户管理器
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        // 3、将第二步创建的UserDetail对象交给UserDetailsManager 管理
        inMemoryUserDetailsManager.createUser(
                //2、创建UserDetail 对象，用于管理用户名、用户密码、用户角色、用户权限
                //下面代码创建了一个用户名为user,密码为123456，角色为user的用户对象
                User.builder()
                        .username("user")
                        .password("123456")
                        .roles("user")
                        .build()
        );
        UserDetails admin = User.builder()
                .username("admin")
                .password("123456")
                .roles("admin")
                .build();
        inMemoryUserDetailsManager.createUser(admin);
        return inMemoryUserDetailsManager;

        //        return username -> {
        //            if ("admin".equals(username)) {
        //                return User.withUsername("admin")
        //                        .password("password")
        //                        .roles("ADMIN")
        //                        .build();
        //            }
        //            throw new UsernameNotFoundException("User not found");
        //        };
    }

    //@Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
        return new InMemoryUserDetailsManager(
                User.withUsername("user")
                        .password("123456")
                        .roles("user")
                        .build()
        );
    }

    /**
     * 注册一个authenticationEventPublisher的bean对象给authentication 事件
     */
    @Bean
    DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher delegate) {
        return new DefaultAuthenticationEventPublisher(delegate);
    }
}
